﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Caliburn.Micro;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Linq;
using NHibernate.Transaction;
using NHibernate.Transform;
using PurchaseSales.Manage.Services;
using Action = System.Action;
using Expression = System.Linq.Expressions.Expression;

namespace PurchaseSales.Manage.Repositories
{
    public class Repository<T> : IRepository<T> where T : class
    {
        public IEnumerator<T> GetEnumerator()
        {
            return GetLinq().GetEnumerator();
        }

        protected ISessionFactory Factory
        {
            get { return IoC.Get<ISessionFactory>(); }
        }

        protected ISession Session
        {
            get { return IoC.Get<ISession>(); }
        }

        private IQueryable<T> GetLinq()
        {
            return Session.Query<T>();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public Expression Expression { get; private set; }
        public Type ElementType { get; private set; }
        public IQueryProvider Provider { get; private set; }
        public T Get(object id)
        {
            return Session.Get<T>(id);
        }

        public T Load(object id)
        {
            return Session.Load<T>(id);
        }

        public IList<T> GetAll()
        {
            return Session.CreateCriteria<T>().List<T>();
        }

        public IList<T> GetAllOrderBy(string property)
        {
            return Session.CreateCriteria<T>().AddOrder(new Order(property, true)).List<T>();
        }

        public IList<TConverted> GetAll<TConverted>(IResultTransformer resultTransformer)
        {
            return Session.CreateCriteria<T>().SetResultTransformer(resultTransformer).List<TConverted>();
        }

        public IEnumerable<T> DelayGetAll()
        {
            return Session.CreateCriteria<T>().Future<T>();
        }

        public T MakePersistent(T entity)
        {
            using (var s = Factory.OpenStatelessSession())
            {
                using (var tx = s.BeginTransaction())
                {
                    Session.SaveOrUpdate(entity);

                    tx.Commit();
                }
            }

            return entity;
        }

        public T MakePersistent<TProperty>(T entity, Expression<Func<TProperty>> propertyExpression)
        {
            //var type = typeof(T);
            //string tableName = type.Name;
            //string propertyName = propertyExpression.GetMemberInfo().Name;
            //var value = propertyExpression.GetValue();
            //var id = type.GetProperty("Id").GetValue(entity, null);

            //Session.CreateQuery(string.Format("update {0} set {1}=:value where Id=:id", tableName, propertyName))
            //    .SetParameter("value", value)
            //    .SetParameter("id", id)
            //    .ExecuteUpdate();
            //return entity;
            throw new NotImplementedException();
        }

        public void Refresh<TChild>(ref TChild entity) where TChild : T
        {
            Session.Evict(entity);
            Session.Refresh(entity);
        }

        public void Refresh<TChild>(TChild entity) where TChild : T
        {
            Session.Refresh(entity);
        }

        public void Clear()
        {
            Session.Clear();
        }

        public void Evict(T entity)
        {
            Session.Evict(entity);
        }

        public void Merge(T entity)
        {
            Session.Merge(entity);
        }

        public virtual void MakeTransient(T entity)
        {
            Session.Delete(entity);
        }
        public void Commit()
        {
            Session.Flush();
        }
        public void MakePersistent(T entity, Action<bool> complete)
        {
            MakePersistent(entity, default(Action), complete);
        }

        public void MakePersistent(T entity, Action before, Action<bool> after)
        {
            Session.Transaction.RegisterSynchronization(new DefaultSynchronization(before, after));
            Session.SaveOrUpdate(entity);
        }

        public void MakePersistent(T entity, Action<T, Exception> exceptionCallBack)
        {
            try
            {
                MakePersistent(entity);
            }
            catch (Exception ex)
            {
                if (exceptionCallBack != null)
                    exceptionCallBack(entity, ex);
            }
        }

        public void BatchInsert<TEntity>(ICollection<TEntity> entities)
        {
            using (var s = Factory.OpenStatelessSession())
            {
                using (var tx = s.BeginTransaction())
                {
                    foreach (var entity in entities)
                    {
                        s.Insert(entity);
                    }
                    tx.Commit();
                }
            }
        }

        public void BatchDetele<TEntity>(ICollection<TEntity> entities)
        {
            using (var s = Factory.OpenStatelessSession())
            {
                using (var tx = s.BeginTransaction())
                {
                    foreach (var entity in entities)
                    {
                        s.Delete(entity);
                    }
                    tx.Commit();
                }
            }
        }

        public bool HaveAnyRelationships(T entity)
        {
            return default(bool);
        }

        public virtual T2 SaveOrUpdate<T2>(T2 entity)
        {
            Session.SaveOrUpdate(entity);
            return entity;
        }

        public T2 Get<T2>(object id)
        {
            return Session.Get<T2>(id);
        }

    }

    public class DefaultSynchronization : ISynchronization
    {
        private readonly Action _beforeCompletion;
        private readonly Action<bool> _afterCompletion;

        public DefaultSynchronization(Action<bool> after)
            : this(default(Action), after)
        { }

        public DefaultSynchronization(Action before, Action<bool> after)
        {
            _beforeCompletion = before;
            _afterCompletion = after;
        }

        public void AfterCompletion(bool success)
        {
            if (_afterCompletion != default(Action<bool>))
                _afterCompletion(success);
        }

        public void BeforeCompletion()
        {
            if (_beforeCompletion != default(Action))
                _beforeCompletion();
        }
    }
}
